/**********************************************************
 * File
 *	badpass.js
 * Description
 *	Creates the badpass object/namespace and provides
 *  initialisation and handling of HTML documents - 
 *  attaching the password strength handler to password
 *  fields and submit handlers to the forms etc.
 *********************************************************/

 var badpass = {
	/*
	 * Name: onLoad()
	 * Desc: Set up a page load listener and initialise the common passwords database.
	 */
	onLoad: function() {
		this.initialized = true;
		this.strings = document.getElementById("badpass-strings");
		this.prefManager = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
		
		//Set a flag indicating whether Gecko 1.9.1+ is in use
		var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo);
		var versionChecker = Components.classes["@mozilla.org/xpcom/version-comparator;1"].getService(Components.interfaces.nsIVersionComparator);
		if(versionChecker.compare(appInfo.platformVersion, "1.9.1") >= 0) {
			//Running Gecko 1.9.1 or later
			this.isAtLeastGecko191 = true;
		} else {
			this.isAtLeastGecko191 = false;
		}
		
		//Initialise the database if this is the first run
		if(this.prefManager.getBoolPref("extensions.badpass.firstrun") == true) {
			badpass.initDatabase();
			this.prefManager.setBoolPref("extensions.badpass.firstrun", false);
		}
		
		//Add a page load listener
		if(typeof(gBrowser) !== 'undefined') {
			gBrowser.addEventListener("load", badpass.onPageLoad, true);
		}
	},
	
	/*
	 * Name: onPageLoad()
	 * Desc: Attaches keyup handlers to password inputs when a new page loads. A DOMNodeInserted
	 *		 event handler is also attached to the document to handle AJAX updates to the page.
	 */
	onPageLoad: function(e) {
		if(e.originalTarget instanceof HTMLDocument) {
			//Attach keyup handlers to password inputs
			badpass.attachKeyupHandlers(e.originalTarget);
			
			//Attach form submit handlers to forms with password inputs
			badpass.attachSubmitHandlers(e.originalTarget);
		}
	},
	
	/*
	 * Name: attachKeyupHandlers()
	 * Desc: Attaches a keyup handler to each password input found in the given document.
	 */
	attachKeyupHandlers: function(d) {
		//Get all input tags
		var inputs = d.getElementsByTagName("input");
		for(var i = 0; i < inputs.length; ++i) {
			//Attach keyup handler to password inputs
			if(inputs[i].type == "password") {
				inputs[i].addEventListener("keyup", function(e) { badpass.handlePasswordKeyup(e); }, true);
			}
		}
	},
	
	/*
	 * Name: attachSubmitHandlers()
	 * Desc: Attaches a submit handler to each form that contains password input elements.
	 */
	attachSubmitHandlers: function(d) {
		//Iterate over all elements of all forms in the document looking for password inputs
		var forms = d.forms;
		for(var i = 0; i < forms.length; ++i) {
			var els = forms[i].elements;
			for(var j = 0; j < els.length; ++j) {
				if(els[j] instanceof HTMLInputElement && els[j].type == "password") {
					forms[i].addEventListener("submit", function(e) { badpass.handleFormSubmit(e); }, false);
					break;
				}
			}
		}
	},
	
	/*
	 * Name: handlePasswordKeyup()
	 * Desc: Sets the colour of the password field based on the password strength.
	 */
	handlePasswordKeyup: function(e) {
		//Get the password text
		var pass = e.originalTarget.value;
		
		//Calculate password strength
		var strength = 0;
		if(badpass.isCommonPassword(pass)) {
			//Common password, weaker than weak!
			strength = 0;
		} else {
			//Calculate password strength
			if(pass.length == 0) {
				strength = 0;
			} else {
				if(/[a-z]/.test(pass)) { ++strength; }
				if(/[A-Z]/.test(pass)) { ++strength; }
				if(/[0-9]/.test(pass)) { ++strength; }
				if(/[^a-zA-Z0-9]/.test(pass)){ ++strength; }
				if(pass.length < 8) { if(strength > 1) { --strength; } } else { if(strength < 4) { ++strength; } }
			}
		}
		
		//Set the background colour of the password field
		switch(strength) {
			case 0:
				e.originalTarget.style.backgroundColor = this.prefManager.getCharPref("extensions.badpass.colour.common");
				break;
				
			case 1:
				e.originalTarget.style.backgroundColor = this.prefManager.getCharPref("extensions.badpass.colour.veryweak");
				break;
				
			case 2:
				e.originalTarget.style.backgroundColor = this.prefManager.getCharPref("extensions.badpass.colour.weak");
				break;
				
			case 3:
				e.originalTarget.style.backgroundColor = this.prefManager.getCharPref("extensions.badpass.colour.medium");
				break;
				
			case 4:
				e.originalTarget.style.backgroundColor = this.prefManager.getCharPref("extensions.badpass.colour.strong");
				break;
		}
	},
	
	/*
	 * Name: handleFormSubmit()
	 * Desc: Handles form submission based on the form submission handling preferences.
	 */
	handleFormSubmit: function(e) {
		//Get the form submit handling preference
		var pref = this.prefManager.getIntPref("extensions.badpass.formsubmithandling");
		
		//Return here if it is disabled
		if(pref == 0) { return; }
		
		//Determine if any relevant password fields do not meet requirements
		var meetsRequirements = true;
		var inputs = e.target.elements;
		for(var i = 0; i < inputs.length; ++i) {
			if(inputs[i] instanceof HTMLInputElement) {
				if(inputs[i].type == "password" && badpass.doesPasswordMeetRequirements(inputs[i].value) == false) {
					meetsRequirements = false;
					break;
				}
			}
		}
		
		//If the password fields meet the requirements then stop here
		if(meetsRequirements == true) { return; }
		
		//Display a warning with a button to open the BadPass preferences
		var notifyBox = gBrowser.getNotificationBox();
		if(notifyBox.getNotificationWithValue("badpass-submit-warning") == null) {
			var prefsButton = new Object();
			prefsButton.label = this.strings.getString("extensions.badpass@nickbloor.co.uk.warning.prefsbuttonlabel");
			prefsButton.accessKey = null;
			prefsButton.popup = null;
			prefsButton.callback = function() { badpass.menuShowPrefs(null); };
			notifyBox.appendNotification(this.strings.getString("extensions.badpass@nickbloor.co.uk.submitwarning.text"),
										 "badpass-submit-warning",
										 "",
										 notifyBox.PRIORITY_WARNING_MEDIUM,
										 [prefsButton]);
		}
		
		//Block the form submission
		if(pref == 2) {
			e.preventDefault();
		}
	},
	
	/*
	 * Name: doesPasswordMeetRequirements()
	 * Desc: Checks whether a password meets the requirements specified in the options dialog.
	 */
	doesPasswordMeetRequirements: function(password) {
		//Check if the password is a common one firstly
		if(badpass.isCommonPassword(password)) { return false; }
		
		//Check the password length
		if(password.length < this.prefManager.getIntPref("extensions.badpass.minimumlength")) { return false; }
		
		//Check the password character set
		if(this.prefManager.getBoolPref("extensions.badpass.requirelowercase") == true) {
			if(/[a-z]/.test(password) == false) { return false; }
		}
		if(this.prefManager.getBoolPref("extensions.badpass.requireuppercase") == true) {
			if(/[A-Z]/.test(password) == false) { return false; }
		}
		if(this.prefManager.getBoolPref("extensions.badpass.requirenumbers") == true) {
			if(/[0-9]/.test(password) == false) { return false; }
		}
		if(this.prefManager.getBoolPref("extensions.badpass.requiresymbols") == true) {
			if(/[^a-zA-Z0-9]/.test(password) == false) { return false; }
		}
		
		//The password meets the requirements
		return true;
	},
	
	/*
	 * Name: menuShowPrefs()
	 * Desc: Displays the preferences dialog when the preferences menu is clicked.
	 */
	menuShowPrefs: function(e) {
		window.openDialog("chrome://badpass/content/options.xul", "", "modal=yes");
	}
};

//Add a window load event listener to initialise BadPass
window.addEventListener("load", function () { badpass.onLoad(); }, false);
